package com.htfyun.eink.pw.draw.pen;

import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.htfyun.eink.pw.configs.ConfigCache;
import com.htfyun.eink.pw.configs.PWConfig;
import com.htfyun.eink.pw.draw.IPWDrawPath;
import com.htfyun.eink.pw.draw.IPWDrawProperty;
import com.htfyun.eink.pw.draw.cache.IPathCache;
import com.htfyun.eink.pw.draw.cache.PathCachePathInfo;
import com.htfyun.eink.pw.draw.pointer.PWPointer;
import com.htfyun.eink.pw.draw.pointer.PWPointerActionEnums;

public abstract class PenRandomBase implements IPen {

    protected final Path pathOfUpdatePW = new Path();

    protected final Rect rectOfUpdatePW = new Rect();
    protected final Rect rectForCheck = new Rect();

    protected float lastX, lastY;

    protected final Paint paintRandom = new Paint();

    protected long eventTimeOfPointerDown;

    protected IPathCache pathCache;

    protected final PathCachePathInfo cachePathInfo = new PathCachePathInfo();

    @Nullable
    @Override
    public IPathCache getPathCache() {
        if (pathCache == null) {
            pathCache = new PathCacheImpl();
        }
        return pathCache;
    }

    public abstract void configPaint(@NonNull Paint paintRandom, @NonNull PWPointer pointer);

    public abstract boolean cacheStrokeWidth() ;

    @NonNull
    @Override
    public PenDrawPointerResultEnums drawPointer(@NonNull IPWDrawPath draw, @NonNull PWPointer pointer) {
        draw.getPaintConfig().configPaint(paintRandom);
        configPaint(paintRandom, pointer);
        return handlePointer(draw, pointer);
    }

    @NonNull
    @Override
    public Rect getDirty() {
        return rectOfUpdatePW;
    }

    public PenDrawPointerResultEnums handlePointer(@NonNull IPWDrawPath draw, @NonNull PWPointer pointer) {
        PWPointerActionEnums actionEnums = pointer.toPWPointerActionEnums();

        switch (actionEnums) {
            case DOWN:
                return pointerDown(draw, pointer);
            case MOVE:
                return pointerMove(draw, pointer);
            case UP:
                return pointerUp(draw, pointer);
            default:
                break;
        }
        return PenDrawPointerResultEnums.NONE;
    }

    public PenDrawPointerResultEnums pointerDown(@NonNull IPWDrawPath draw, @NonNull PWPointer pointer) {
        float x = pointer.x;
        float y = pointer.y;
        lastX = x;
        lastY = y;
        rectOfUpdatePW.setEmpty();
        rectForCheck.setEmpty();

        eventTimeOfPointerDown = pointer.eventTimeCache;
        cachePathInfo.reset();
        cachePathInfo.setEraser(this instanceof IPenEraser);
        return PenDrawPointerResultEnums.NONE;
    }

    public PenDrawPointerResultEnums pointerMove(@NonNull IPWDrawPath draw, @NonNull PWPointer pointer) {
        return pointerMoveOrUp(draw, pointer, true);
    }

    public PenDrawPointerResultEnums pointerUp(@NonNull IPWDrawPath draw, @NonNull PWPointer pointer) {
        return pointerMoveOrUp(draw, pointer, false);
    }

    public PenDrawPointerResultEnums pointerMoveOrUp(@NonNull IPWDrawPath draw, @NonNull PWPointer pointer, boolean move) {
        float x = pointer.x;
        float y = pointer.y;

        if (x == lastX && y == lastY) {
            return PenDrawPointerResultEnums.NONE;
        }

        pathOfUpdatePW.reset();
        pathOfUpdatePW.moveTo(lastX, lastY);
        pathOfUpdatePW.quadTo((lastX + x) / 2.0f, (lastY + y) / 2.0f, x, y);

        draw.drawPath(pathOfUpdatePW, paintRandom, rectOfUpdatePW);

        lastX = x;
        lastY = y;

        //for cache
        cachePathInfo.addPath(pathOfUpdatePW);
        if (cacheStrokeWidth()) {
            cachePathInfo.addTraceStrokeWidth(paintRandom.getStrokeWidth());
        }
        rectForCheck.union(rectOfUpdatePW);
        if (rectForCheck.width() > ConfigCache.RECT_CHECK_MIN_SIZE
                || rectForCheck.height() > ConfigCache.RECT_CHECK_MIN_SIZE
                || !move) {
            cachePathInfo.addRectForCheck(rectForCheck);
            rectForCheck.setEmpty();
        }

        if (draw instanceof IPWDrawProperty) {
            IPWDrawProperty impl = (IPWDrawProperty) draw;
            if (PWConfig.Instance.configLog.isShowRectOfUpdatePW()) {
                impl.getCanvasPW().drawRect(rectOfUpdatePW, paintRandom);
            }
        }

        return PenDrawPointerResultEnums.INVALIDATE_PW_DIRTY;
    }

    protected class PathCacheImpl implements IPathCache {

        @Override
        public long getEventTimeOfPointerDown() {
            return eventTimeOfPointerDown;
        }

        @Override
        public boolean canCachePointerByAction(@NonNull PWPointerActionEnums actionEnums) {
            return true;
        }

        @NonNull
        @Override
        public String getPenId() {
            return PenRandomBase.this.getPenId();
        }

        @NonNull
        @Override
        public PathCachePathInfo getCachePathInfo() {
            return cachePathInfo;
        }

    }

}
